/* Implements pcb_list.h */

#include "pcb_list.h"

/* Looks for the first free pid using p_list, inserts pcb at the correct position after having assigned its pid and returns its pid in pid. Returns SUCCESS or OUT_OF_MEMORY. */
int32_t pcb_list_add(pcb_list* p_list, PCB* pcb, int32_t* pid){
  pcb_list current, prev = NULL, new_l;
  int32_t current_pid = FIRST_PID; 
  
  for(current = *p_list; current != NULL && pcb_get_pid(current->pcb) == current_pid; prev = current, current = current->next, current_pid ++);
  //current_pid is the pid we are looking for 
  new_l = malloc(sizeof(pcb_link), SYSTEM_ID);
  if(new_l == NULL){
    return OUT_OF_MEMORY;
  }
  pcb_set_pid(pcb, current_pid);
  new_l->pcb = pcb;
  new_l->next = NULL;
  if(prev == NULL){
    //it's the first one
    new_l->next = *p_list;
    *p_list = new_l;
  }else{
    //somewhere in the middle of the list
    new_l->next = prev->next;
    prev->next = new_l;
  }
  *pid = current_pid;
  return SUCCESS;
}

/* removes pcb from p_list. Returns SUCCESS or FAILURE if pid is not in the list or if the free failed */
int32_t pcb_list_remove(pcb_list* p_list, PCB* pcb){
  pcb_list current, prev = NULL;
  int32_t pid = pcb_get_pid(pcb);

  for(current = *p_list; current != NULL && pcb_get_pid(current->pcb) < pid; prev = current, current = current->next);
  //the list is sorted by PID. So, now, either we found pcb or it is not in the list
  if(current != NULL && pcb_get_pid(current->pcb) == pid){
    //found it
    if(prev == NULL)
      //first one
      *p_list = (*p_list)->next;
    else
      prev->next = current->next;
    if(free_pcb(current->pcb) == SUCCESS){
      return free(current, SYSTEM_ID);
    }
    return FAILURE;
  }else
    //not found
    return FAILURE;
}

/* returns the number of elements in p_list */
int32_t pcb_list_count(pcb_list p_list){
  if(p_list == NULL)
    return 0;
  return 1 + pcb_list_count(p_list->next);
}

/* returns an array containing all the PID in p_list and writes their number in length. It returns NULL if the system has run out of memory. */
int32_t* pcb_list_get_all_pid(pcb_list p_list, int32_t* length, int32_t owner){
  int32_t count = pcb_list_count(p_list);
  
  //we allocate the array on behalf of the caller
  int32_t* pid_array = malloc(count*sizeof(int32_t),owner);
  if(pid_array == NULL)
    return NULL; // out of memory
  
  int32_t i = 0;
  for(; p_list != NULL; p_list = p_list->next){
    pid_array[i++] = pcb_get_pid(p_list->pcb);
  }
  *length = count;
  return pid_array;
}
